package com.haohan.cloud.scm.common.tools.util;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisException;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Jedis Cache 工具类
 *
 * @author ThinkGem
 * @version 2014-6-29
 */
@Slf4j
@Component
public class JedisUtils {

  @Autowired
  private JedisPool jedisPool;

  /**
   * 获取缓存
   *
   * @param key 键
   * @return 值
   */
  public  String get(String key) {
    String value = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(key)) {
        value = jedis.get(key);
        value = StrUtil.isNotBlank(value) && !"nil".equalsIgnoreCase(value) ? value : null;
        log.debug("get {} = {}", key, value);
      }
    } catch (Exception e) {
      log.warn("get {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return value;
  }

  /**
   * 获取缓存
   *
   * @param key 键
   * @return 值
   */
  public  Object getObject(String key) {
    Object value = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(getBytesKey(key))) {
        value = toObject(jedis.get(getBytesKey(key)));
        log.debug("getObject {} = {}", key, value);
      }
    } catch (Exception e) {
      log.warn("getObject {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return value;
  }

  /**
   * 设置缓存
   *
   * @param key          键
   * @param value        值
   * @param cacheSeconds 超时时间，0为不超时
   * @return
   */
  public  String set(String key, String value, int cacheSeconds) {
    String result = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      result = jedis.set(key, value);
      if (cacheSeconds != 0) {
        jedis.expire(key, cacheSeconds);
      }
      log.debug("set {} = {}", key, value);
    } catch (Exception e) {
      log.warn("set {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 设置缓存
   *
   * @param key          键
   * @param value        值
   * @param cacheSeconds 超时时间，0为不超时
   * @return
   */
  public  String setObject(String key, Object value, int cacheSeconds) {
    String result = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      result = jedis.set(getBytesKey(key), toBytes(value));
      if (cacheSeconds != 0) {
        jedis.expire(key, cacheSeconds);
      }
      log.debug("setObject {} = {}", key, value);
    } catch (Exception e) {
      log.warn("setObject {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 获取List缓存
   *
   * @param key 键
   * @return 值
   */
  public  List<String> getList(String key) {
    List<String> value = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(key)) {
        value = jedis.lrange(key, 0, -1);
        log.debug("getList {} = {}", key, value);
      }
    } catch (Exception e) {
      log.warn("getList {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return value;
  }

  /**
   * 获取List缓存
   *
   * @param key 键
   * @return 值
   */
  public  List<Object> getObjectList(String key) {
    List<Object> value = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(getBytesKey(key))) {
        List<byte[]> list = jedis.lrange(getBytesKey(key), 0, -1);
        value = Lists.newArrayList();
        for (byte[] bs : list) {
          value.add(toObject(bs));
        }
        log.debug("getObjectList {} = {}", key, value);
      }
    } catch (Exception e) {
      log.warn("getObjectList {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return value;
  }

  /**
   * 设置List缓存
   *
   * @param key          键
   * @param value        值
   * @param cacheSeconds 超时时间，0为不超时
   * @return
   */
  public  long setList(String key, List<String> value, int cacheSeconds) {
    long result = 0;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(key)) {
        jedis.del(key);
      }
      result = jedis.rpush(key, (String[]) value.toArray(new String[value.size()]));
      if (cacheSeconds != 0) {
        jedis.expire(key, cacheSeconds);
      }
      log.debug("setList {} = {}", key, value);
    } catch (Exception e) {
      log.warn("setList {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 设置List缓存
   *
   * @param key          键
   * @param value        值
   * @param cacheSeconds 超时时间，0为不超时
   * @return
   */
  public  long setObjectList(String key, List<Object> value, int cacheSeconds) {
    long result = 0;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(getBytesKey(key))) {
        jedis.del(key);
      }
      List<byte[]> list = Lists.newArrayList();
      for (Object o : value) {
        list.add(toBytes(o));
      }
      byte[] bytesKey = getBytesKey(key);
      result = jedis.rpush(bytesKey, (byte[][]) list.toArray());
      if (cacheSeconds != 0) {
        jedis.expire(key, cacheSeconds);
      }
      log.debug("setObjectList {} = {}", key, value);
    } catch (Exception e) {
      log.warn("setObjectList {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 向List缓存中添加值
   *
   * @param key   键
   * @param value 值
   * @return
   */
  public  long listAdd(String key, String... value) {
    long result = 0;
    Jedis jedis = null;
    try {
      jedis = getResource();
      result = jedis.rpush(key, value);
      log.debug("listAdd {} = {}", key, value);
    } catch (Exception e) {
      log.warn("listAdd {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 向List缓存中添加值
   *
   * @param key   键
   * @param value 值
   * @return
   */
  public  long listObjectAdd(String key, Object... value) {
    long result = 0;
    Jedis jedis = null;
    try {
      jedis = getResource();
      List<byte[]> list = Lists.newArrayList();
      for (Object o : value) {
        list.add(toBytes(o));
      }
      result = jedis.rpush(getBytesKey(key), (byte[][]) list.toArray());
      log.debug("listObjectAdd {} = {}", key, value);
    } catch (Exception e) {
      log.warn("listObjectAdd {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 删除key，可以是一个，也可以是多个key
   *
   * @param keys
   */
  public synchronized  void deleteKey(String... keys) {
    getResource().del(keys);
  }

  /**
   * 删除匹配的key<br>
   * 如以my为前缀的则 参数为"my*"
   */
  public synchronized  void deleteKeys(String pattern) {
    //列出所有匹配的key
    Set<String> keySet = getResource().keys(pattern);
    if (keySet == null || keySet.size() <= 0) {
      return;
    }
    String[] keyArr = new String[keySet.size()];
    int i = 0;
    for (String keys : keySet) {
      keyArr[i] = keys;
      i++;
    }
    deleteKey(keyArr);
  }

  /**
   * 删除前缀为{参数}的所有key<br>
   *
   * @param prefix
   */
  public synchronized  void deleteKeyByPrefix(String prefix) {
    deleteKeys(prefix + "*");
  }


  /**
   * 删除包含{参数}的所有key<br>
   *
   * @param contain
   */
  public synchronized  void deleteKeyByContain(String contain) {
    deleteKeys("*" + contain + "*");
  }

  /**
   * 删除当前中所有key
   */
  public synchronized  void flushdb() {
    getResource().flushDB();
  }


  /**
   * 获取缓存
   *
   * @param key 键
   * @return 值
   */
  public  Set<String> getSet(String key) {
    Set<String> value = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(key)) {
        value = jedis.smembers(key);
        log.debug("getSet {} = {}", key, value);
      }
    } catch (Exception e) {
      log.warn("getSet {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return value;
  }

  /**
   * 获取缓存
   *
   * @param key 键
   * @return 值
   */
  public  Set<Object> getObjectSet(String key) {
    Set<Object> value = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(getBytesKey(key))) {
        value = Sets.newHashSet();
        Set<byte[]> set = jedis.smembers(getBytesKey(key));
        for (byte[] bs : set) {
          value.add(toObject(bs));
        }
        log.debug("getObjectSet {} = {}", key, value);
      }
    } catch (Exception e) {
      log.warn("getObjectSet {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return value;
  }

  /**
   * 设置Set缓存
   *
   * @param key          键
   * @param value        值
   * @param cacheSeconds 超时时间，0为不超时
   * @return
   */
  public  long setSet(String key, Set<String> value, int cacheSeconds) {
    long result = 0;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(key)) {
        jedis.del(key);
      }
      result = jedis.sadd(key, (String[]) value.toArray(new String[value.size()]));
      if (cacheSeconds != 0) {
        jedis.expire(key, cacheSeconds);
      }
      log.debug("setSet {} = {}", key, value);
    } catch (Exception e) {
      log.warn("setSet {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 设置Set缓存
   *
   * @param key          键
   * @param value        值
   * @param cacheSeconds 超时时间，0为不超时
   * @return
   */
  public  long setObjectSet(String key, Set<Object> value, int cacheSeconds) {
    long result = 0;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(getBytesKey(key))) {
        jedis.del(key);
      }
      Set<byte[]> set = Sets.newHashSet();
      for (Object o : value) {
        set.add(toBytes(o));
      }
      result = jedis.sadd(getBytesKey(key), (byte[][]) set.toArray());
      if (cacheSeconds != 0) {
        jedis.expire(key, cacheSeconds);
      }
      log.debug("setObjectSet {} = {}", key, value);
    } catch (Exception e) {
      log.warn("setObjectSet {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 向Set缓存中添加值
   *
   * @param key   键
   * @param value 值
   * @return
   */
  public  long setSetAdd(String key, String... value) {
    long result = 0;
    Jedis jedis = null;
    try {
      jedis = getResource();
      result = jedis.sadd(key, value);
      log.debug("setSetAdd {} = {}", key, value);
    } catch (Exception e) {
      log.warn("setSetAdd {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 向Set缓存中添加值
   *
   * @param key   键
   * @param value 值
   * @return
   */
  public  long setSetObjectAdd(String key, Object... value) {
    long result = 0;
    Jedis jedis = null;
    try {
      jedis = getResource();
      Set<byte[]> set = Sets.newHashSet();
      for (Object o : value) {
        set.add(toBytes(o));
      }
      result = jedis.rpush(getBytesKey(key), (byte[][]) set.toArray());
      log.debug("setSetObjectAdd {} = {}", key, value);
    } catch (Exception e) {
      log.warn("setSetObjectAdd {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 获取Map缓存
   *
   * @param key 键
   * @return 值
   */
  public  Map<String, String> getMap(String key) {
    Map<String, String> value = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(key)) {
        value = jedis.hgetAll(key);
        log.debug("getMap {} = {}", key, value);
      }
    } catch (Exception e) {
      log.warn("getMap {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return value;
  }

  /**
   * 获取Map缓存
   *
   * @param key 键
   * @return 值
   */
  public  Map<String, Object> getObjectMap(String key) {
    Map<String, Object> value = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(getBytesKey(key))) {
        value = Maps.newHashMap();
        Map<byte[], byte[]> map = jedis.hgetAll(getBytesKey(key));
        for (Map.Entry<byte[], byte[]> e : map.entrySet()) {
          value.put(StrUtil.toString(e.getKey()), toObject(e.getValue()));
        }
        log.debug("getObjectMap {} = {}", key, value);
      }
    } catch (Exception e) {
      log.warn("getObjectMap {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return value;
  }

  /**
   * 设置Map缓存
   *
   * @param key          键
   * @param value        值
   * @param cacheSeconds 超时时间，0为不超时
   * @return
   */
  public  String setMap(String key, Map<String, String> value, int cacheSeconds) {
    String result = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(key)) {
        jedis.del(key);
      }
      result = jedis.hmset(key, value);
      if (cacheSeconds != 0) {
        jedis.expire(key, cacheSeconds);
      }
      log.debug("setMap {} = {}", key, value);
    } catch (Exception e) {
      log.warn("setMap {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 设置Map缓存
   *
   * @param key          键
   * @param value        值
   * @param cacheSeconds 超时时间，0为不超时
   * @return
   */
  public  String setObjectMap(String key, Map<String, Object> value, int cacheSeconds) {
    String result = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(getBytesKey(key))) {
        jedis.del(key);
      }
      Map<byte[], byte[]> map = Maps.newHashMap();
      for (Map.Entry<String, Object> e : value.entrySet()) {
        map.put(getBytesKey(e.getKey()), toBytes(e.getValue()));
      }
      result = jedis.hmset(getBytesKey(key), (Map<byte[], byte[]>) map);
      if (cacheSeconds != 0) {
        jedis.expire(key, cacheSeconds);
      }
      log.debug("setObjectMap {} = {}", key, value);
    } catch (Exception e) {
      log.warn("setObjectMap {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 向Map缓存中添加值
   *
   * @param key   键
   * @param value 值
   * @return
   */
  public  String mapPut(String key, Map<String, String> value) {
    String result = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      result = jedis.hmset(key, value);
      log.debug("mapPut {} = {}", key, value);
    } catch (Exception e) {
      log.warn("mapPut {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 向Map缓存中添加值
   *
   * @param key   键
   * @param value 值
   * @return
   */
  public  String mapObjectPut(String key, Map<String, Object> value) {
    String result = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      Map<byte[], byte[]> map = Maps.newHashMap();
      for (Map.Entry<String, Object> e : value.entrySet()) {
        map.put(getBytesKey(e.getKey()), toBytes(e.getValue()));
      }
      result = jedis.hmset(getBytesKey(key), (Map<byte[], byte[]>) map);
      log.debug("mapObjectPut {} = {}", key, value);
    } catch (Exception e) {
      log.warn("mapObjectPut {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 移除Map缓存中的值
   *
   * @param key    键
   * @param mapKey 值
   * @return
   */
  public  long mapRemove(String key, String mapKey) {
    long result = 0;
    Jedis jedis = null;
    try {
      jedis = getResource();
      result = jedis.hdel(key, mapKey);
      log.debug("mapRemove {}  {}", key, mapKey);
    } catch (Exception e) {
      log.warn("mapRemove {}  {}", key, mapKey, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 移除Map缓存中的值
   *
   * @param key 键
   * @return
   */
  public  long mapObjectRemove(String key, String mapKey) {
    long result = 0;
    Jedis jedis = null;
    try {
      jedis = getResource();
      result = jedis.hdel(getBytesKey(key), getBytesKey(mapKey));
      log.debug("mapObjectRemove {}  {}", key, mapKey);
    } catch (Exception e) {
      log.warn("mapObjectRemove {}  {}", key, mapKey, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 判断Map缓存中的Key是否存在
   *
   * @param key 键
   * @return
   */
  public  boolean mapExists(String key, String mapKey) {
    boolean result = false;
    Jedis jedis = null;
    try {
      jedis = getResource();
      result = jedis.hexists(key, mapKey);
      log.debug("mapExists {}  {}", key, mapKey);
    } catch (Exception e) {
      log.warn("mapExists {}  {}", key, mapKey, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 判断Map缓存中的Key是否存在
   *
   * @param key    键
   * @param mapKey 值
   * @return
   */
  public  boolean mapObjectExists(String key, String mapKey) {
    boolean result = false;
    Jedis jedis = null;
    try {
      jedis = getResource();
      result = jedis.hexists(getBytesKey(key), getBytesKey(mapKey));
      log.debug("mapObjectExists {}  {}", key, mapKey);
    } catch (Exception e) {
      log.warn("mapObjectExists {}  {}", key, mapKey, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 删除缓存
   *
   * @param key 键
   * @return
   */
  public  long del(String key) {
    long result = 0;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(key)) {
        result = jedis.del(key);
        log.debug("del {}", key);
      } else {
        log.debug("del {} not exists", key);
      }
    } catch (Exception e) {
      log.warn("del {}", key, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 删除缓存
   *
   * @param key 键
   * @return
   */
  public  long delObject(String key) {
    long result = 0;
    Jedis jedis = null;
    try {
      jedis = getResource();
      if (jedis.exists(getBytesKey(key))) {
        result = jedis.del(getBytesKey(key));
        log.debug("delObject {}", key);
      } else {
        log.debug("delObject {} not exists", key);
      }
    } catch (Exception e) {
      log.warn("delObject {}", key, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 缓存是否存在
   *
   * @param key 键
   * @return
   */
  public  boolean exists(String key) {
    boolean result = false;
    Jedis jedis = null;
    try {
      jedis = getResource();
      result = jedis.exists(key);
      log.debug("exists {}", key);
    } catch (Exception e) {
      log.warn("exists {}", key, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 缓存是否存在
   *
   * @param key 键
   * @return
   */
  public  boolean existsObject(String key) {
    boolean result = false;
    Jedis jedis = null;
    try {
      jedis = getResource();
      result = jedis.exists(getBytesKey(key));
      log.debug("existsObject {}", key);
    } catch (Exception e) {
      log.warn("existsObject {}", key, e);
    } finally {
      returnResource(jedis);
    }
    return result;
  }

  /**
   * 获取资源
   *
   * @return
   * @throws JedisException
   */
  public  Jedis getResource() throws JedisException {
    Jedis jedis = null;
    try {
      jedis = jedisPool.getResource();
    } catch (JedisException e) {
      log.warn("getResource.", e);
      returnBrokenResource(jedis);
      throw e;
    }
    return jedis;
  }

  /**
   * 归还资源
   *
   * @param jedis
   * @param
   */
  public static void returnBrokenResource(Jedis jedis) {
    if (jedis != null) {
      jedis.close();
    }
  }

  /**
   * 释放资源
   *
   * @param jedis
   * @param
   */
  public static void returnResource(Jedis jedis) {
    if (jedis != null) {
      jedis.close();
    }
  }

  /**
   * 获取byte[]类型Key
   *
   * @param
   * @return
   */
  public static byte[] getBytesKey(Object object) {
    if (object instanceof String) {
      return StrUtil.bytes((String) object);
    } else {
      return ObjectUtil.serialize(object);
    }
  }

  /**
   * 获取byte[]类型Key
   *
   * @param key
   * @return
   */
  public static Object getObjectKey(byte[] key) {
    try {
      return StrUtil.toString(key);
    } catch (UnsupportedOperationException uoe) {
      try {
        return JedisUtils.toObject(key);
      } catch (UnsupportedOperationException uoe2) {
        uoe2.printStackTrace();
      }
    }
    return null;
  }

  /**
   * Object转换byte[]类型
   *
   * @param
   * @return
   */
  public static byte[] toBytes(Object object) {
    return ObjectUtil.serialize(object);
  }

  /**
   * byte[]型转换Object
   *
   * @param
   * @return
   */
  public static Object toObject(byte[] bytes) {
    return ObjectUtil.unserialize(bytes);
  }

  /**
   * 获取 自增num
   *
   * @param key      键
   * @param startNum 初始数值
   * @return
   */
  public  Long fetchIncrNum(String key, int startNum) {
    Long num = null;
    String value = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      num = jedis.incr(key);
      if (num <= startNum) {
        num = startNum + 1L;
        value = String.valueOf(num);
        jedis.set(key, value);
      }
      log.debug("set {} = {}", key, num);
    } catch (Exception e) {
      log.warn("set {} = {}", key, value, e);
    } finally {
      returnResource(jedis);
    }
    return num;
  }

  /**
   * 以秒为单位，返回给定 key 的剩余生存时间(TTL, time to live)
   *
   * @param key
   * @return
   */
  public  Long ttl(String key) {
    Long num = null;
    Jedis jedis = null;
    try {
      jedis = getResource();
      num = jedis.ttl(key);
      log.debug("ttl {} = {}", key, num);
    } catch (Exception e) {
      log.warn("ttl {} = {}", key, num, e);
    } finally {
      returnResource(jedis);
    }
    return num;
  }

}

